﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Sustainalytics.ClientDW.DataAccessLayer;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.DataLayer.Shared;
using Sustainalytics.Entities;
using Sustainalytics.Entities.ConnectionStrings;
using Sustainalytics.ReportGenerator.BL;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using System.Threading;
using System.Collections.Concurrent;
using System.IO;

namespace MongoDB_Stress_Tool
{
    class Program
    {
        private static string _clientDwConnectionString;
        private static string _clientPlatformConnectionString;
        private static string _dwArchiveConnectionString;


        private static Dictionary<int, CompanyProfile> _companyProfiles = new Dictionary<int, CompanyProfile>();
        private static ConcurrentBag<string> _log = new ConcurrentBag<string>();
        private static int _index;

        public static bool _stopProcess = false;

        private class ConnectionString
        {
            public ConnectionStringType Id { get; set; }
            public string Value { get; set; }
            public DateTime Timestamp { get; set; }
        }

        static void Main(string[] args)
        {
            SetConnectionStrings();

            var clientDwRepo = ClientDwRepository.GetInstance(_clientDwConnectionString);
            var companyProfiles = clientDwRepo.GetCompanyProfiles();
            var companies = companyProfiles.Where(c => c.RootPeerGroup.Contains("Oil & Gas Producer"))
                .OrderBy(c => c.CompanyName)
                .ToList();

            var peerGroups = companyProfiles.GroupBy(c => c.RootPeerGroup)
                .ToDictionary(g => g.Key, g => g.Select(c => c.CompanyId).ToList())
                .ToList();

            foreach (var peerGroup in peerGroups.OrderBy(g => g.Key))
            {
                var sw = Stopwatch.StartNew();

                var crdo = GetCompanyReportDataOffline(peerGroup.Value);

                sw.Stop();
                _log.Add(string.Format("{0}, {1}", peerGroup.Key, sw.Elapsed));
                Console.Clear();
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine(string.Format("{0}, {1}, {2}", peerGroup.Key, sw.Elapsed, sw.ElapsedMilliseconds));
            }

            //foreach (var company in companies)
            //{
            //    var sw = Stopwatch.StartNew();

            //    var crdo = GetCompanyReportDataOffline(new List<Guid> { company.CompanyId });

            //    sw.Stop();
            //    _log.Add(string.Format("{0}, {1}", company.CompanyName, sw.Elapsed));
                
            //    Console.Clear();
            //    Console.ForegroundColor = ConsoleColor.Green;
            //    Console.WriteLine(string.Format("{0}, {1}", sw.Elapsed, company.CompanyName));
            //    Console.ForegroundColor = ConsoleColor.Blue;
            //    Console.WriteLine("Press ESC to stop");
            //    if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)
            //    {
            //        break;
            //    }
            //}

            //var index = 0;
            //_companyProfiles.Clear();
            //companyProfiles.ForEach(c =>
            //{
            //    _companyProfiles.Add(index, c);
            //    index++;
            //});

            //_index = 0;

            //Sequential();

            //Clock();


            var results = _log.ToList();

            var sb = new StringBuilder();

            results.ForEach(log => sb.AppendLine(log));

            File.WriteAllText("D:\\Results.log", sb.ToString());
 
        }

        private static void Sequential()
        {
            while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape))
            {
                QueryMongoDB(null);
            }
        }

        private static void Clock()
        {
            Thread ConsoleKeyListener = new Thread(new ThreadStart(KeyBoardEvent));
            ConsoleKeyListener.Name = "KeyListener";
            ConsoleKeyListener.Start();

            var period = Convert.ToInt32(ConfigurationManager.AppSettings["SyncInterval"]);

            var timer = new Timer(QueryMongoDB, null, 0, period * 1000);

            while (true)
            {
                if (_stopProcess)
                {
                    ConsoleKeyListener.Abort();
                    return;
                }
            } 
        }

        public static void KeyBoardEvent()
        {
            do
            {
                if (Console.ReadKey(true).Key == ConsoleKey.Escape)
                {
                    _stopProcess = true;
                }
            } while (true);
        }

        private static void SetConnectionStrings()
        {
            if (ConfigurationManager.ConnectionStrings["ClientPlatform"] == null)
                throw new Exception("ClientPlatform key is missing!");

            _clientPlatformConnectionString = ConfigurationManager.ConnectionStrings["ClientPlatform"].ConnectionString;

            var url = MongoUrl.Create(_clientPlatformConnectionString);

            if (string.IsNullOrWhiteSpace(url.DatabaseName))
                throw new ArgumentException("No database specified in the connection string!");

            var client = new MongoClient(url);
            var server = client.GetServer();

            var database = server.GetDatabase(url.DatabaseName);

            var clientDw = database.GetCollection<ConnectionString>(typeof(ConnectionString).Name)
                .Find(Query<ConnectionString>.Where(cs => cs.Id == ConnectionStringType.ClientDw))
                .SetFields(Fields<ConnectionString>.Include(cs => cs.Value).Exclude(cs => cs.Id))
                .Select(cs => cs.Value)
                .SingleOrDefault();

            if (clientDw != null && clientDw.Contains("ClientDW_A"))
            {
                _clientDwConnectionString = _clientPlatformConnectionString.Replace("ClientPlatform", "ClientDW_A");
                _dwArchiveConnectionString = _clientPlatformConnectionString.Replace("ClientPlatform", "DWArchive_A");
            }
            if (clientDw != null && clientDw.Contains("ClientDW_B"))
            {
                _clientDwConnectionString = _clientPlatformConnectionString.Replace("ClientPlatform", "ClientDW_B");
                _dwArchiveConnectionString = _clientPlatformConnectionString.Replace("ClientPlatform", "DWArchive_B");
            }
        }

        private static void QueryMongoDB(object state)
        {
            SetConnectionStrings();

            var companyProfile = _companyProfiles[_index];

            var sw = Stopwatch.StartNew();

            var crdo = GetCompanyReportDataOffline(new List<Guid> { companyProfile.CompanyId });

            Console.Clear();

            sw.Stop();
            Console.ForegroundColor = ConsoleColor.Green;
            _log.Add(string.Format("{0}, {1}, {2}", DateTime.Now, sw.Elapsed, companyProfile.CompanyName));
            Console.WriteLine(string.Format("Timestamp: {0}, Elapsed time: {1}, Company:{2}", DateTime.Now, sw.Elapsed, companyProfile.CompanyName));
            
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine("Press ESC to stop");

            _index++;
       }

        private static List<CompanyReportDataOffline> GetCompanyReportDataOffline(List<Guid> companyIds)
        {

            var defaultEsgProfile = (new RepositoryCollection<ESGProfile>(_clientPlatformConnectionString))
                .ReadAllWhereFieldEqualsValue("Name", "Default", null, 1).SingleOrDefault();


            var corporateDataRepo = new RepositoryCollection<CorporateData>(_clientDwConnectionString);
            var corporateData = corporateDataRepo.ReadbyIds(companyIds);

            var financialDataRepo = new RepositoryCollection<FinancialData>(_clientDwConnectionString);
            var financialData = financialDataRepo.ReadbyIds(companyIds, new string[] { "MarketCapitalization", "CompanyName" });

            var referenceDataRep = new RepositoryCollection<ReferenceData>(_clientDwConnectionString);
            var referenceData = referenceDataRep.ReadbyIds(companyIds);

            var companyPerformanceRepo = new RepositoryCollection<CompanyPerformance>(_clientDwConnectionString);
            var companyPerformance = companyPerformanceRepo.ReadbyIds(companyIds);

            var companyHistoricalPerformanceRepo = new RepositoryCollection<CompanyHistoricalPerformance>(_dwArchiveConnectionString);
            var companyHistoricalPerformance = companyHistoricalPerformanceRepo.ReadbyIds(companyIds);

            var indicatorTemplatesRep = new RepositoryCollection<IndicatorTemplate>(_clientDwConnectionString);
            var indicatorTemplates = indicatorTemplatesRep.ReadAll();

            var analystRepo = new RepositoryCollection<Analyst>(_clientDwConnectionString);
            var analysts = analystRepo.ReadAll().ToDictionary(k => k.AnalystId, v => v);

            var gicsSubIndustryRepo = new RepositoryCollection<GICSSubIndustry>(_clientDwConnectionString);
            var gicsSubIndustries = gicsSubIndustryRepo.ReadAll().ToDictionary(k => k.Name, v => Convert.ToInt32(v.Number));

            var subIndustryKeyEsgIssues = new Dictionary<int, List<SubIndustryKeyEsgIssue>>();
            var subIndustryKeyEsgIssueRepo = new RepositoryCollection<SubIndustryKeyEsgIssue>(_clientDwConnectionString);
            var allSubIndustryKeyEsgIssues = subIndustryKeyEsgIssueRepo.ReadAll();
            var subIndustries = allSubIndustryKeyEsgIssues.GroupBy(x => x.SubIndustryCode).ToList();
            subIndustries.ForEach(x => subIndustryKeyEsgIssues.Add(x.Key, x.ToList()));

            var companyKeyEsgIssueRepo = new RepositoryCollection<CompanyKeyESGIssue>(_clientDwConnectionString);
            var companyKeyEsgIssues = companyKeyEsgIssueRepo.ReadAllWhere(x => companyIds.Contains(x.Id)).ToList();
            var keyEsgIssuesDictionary = companyKeyEsgIssues.ToDictionary(kei => kei.Id, kei => kei.KeyESGIssues);
            var subIndustryCodeDictionary = companyKeyEsgIssues.ToDictionary(kei => kei.Id, kei => kei.SubIndustryCode);

            var standardIndicatorsRepo = new RepositoryCollection<RPIndicatorExtended>(_clientDwConnectionString);
            var allStandardIndicators = standardIndicatorsRepo.ReadAllWhere(x => companyIds.Contains(x.CompanyId)).ToList();
            var standardIndicators = allStandardIndicators.GroupBy(a => a.CompanyId).ToDictionary(si => si.Key, si => si.ToList());

            var incidentsRepo = new RepositoryCollection<Incident>(_clientDwConnectionString);
            var allIncidents = incidentsRepo.ReadAllWhere(x => companyIds.Contains(x.CompanyId)).ToList();
            var incidents = allIncidents.GroupBy(a => a.CompanyId).ToDictionary(i => i.Key, i => i.ToList());

            var eventIndicatorsRepo = new RepositoryCollection<EventIndicator>(_clientDwConnectionString);
            var allEventIndicators = eventIndicatorsRepo.ReadAllWhere(x => companyIds.Contains(x.CompanyId)).ToList();
            var eventIndicators = allEventIndicators.GroupBy(a => a.CompanyId).ToDictionary(ei => ei.Key, ei => ei.ToList());


            var controversyIndicatorsRepo = new RepositoryCollection<RPControversyIndicator>(_clientDwConnectionString);
            var companyControversyIndicators = controversyIndicatorsRepo.ReadAllWhere(x => companyIds.Contains(x.CompanyId)).ToList();
            var controversyIndicators = companyControversyIndicators.GroupBy(a => a.CompanyId).ToDictionary(ci => ci.Key, ci => ci.ToList());

            var comparisonGroupControversyRepo = new RepositoryCollection<ComparisonGroupControversy>(_clientDwConnectionString);
            var companyComparisonGroupControversies = comparisonGroupControversyRepo.ReadAllWhere(x => companyIds.Contains(x.CompanyId)).ToList();
            var comparisonGroupControversies = companyComparisonGroupControversies.GroupBy(a => a.CompanyId).ToDictionary(cgc => cgc.Key, cgc => cgc.ToList());


            var sourcesRep = new RepositoryCollection<Source>(_clientDwConnectionString);
            var allSources = sourcesRep.ReadAllWhere(s => companyIds.Contains(s.CompanyId));
            var sources = allSources.GroupBy(s => s.CompanyId).ToDictionary(cs => cs.Key, cs => cs.OrderBy(s => s.SourceText).ToList());


            var crdos = new List<CompanyReportDataOffline>();

            foreach (var companyId in companyIds)
            {
                var keyEsgIssues = keyEsgIssuesDictionary.ContainsKey(companyId) ? keyEsgIssuesDictionary[companyId] : null;

                var subIndustryName = corporateData[companyId].GICSSubIndustry;
                var subIndustryCode = subIndustryCodeDictionary.ContainsKey(companyId)
                    ? subIndustryCodeDictionary[companyId]
                    : gicsSubIndustries.ContainsKey(subIndustryName)
                        ? gicsSubIndustries[subIndustryName]
                        : 0;

                var crdo = new CompanyReportDataOffline();

                crdo.CompanyId = companyId;

                crdo.ESGProfile = defaultEsgProfile;

                crdo.CorporateData = corporateData[companyId];
                crdo.ReferenceData = referenceData[companyId];
                crdo.FinancialData = financialData[companyId];

                crdo.OverallPerformance = companyPerformance.ContainsKey(companyId)
                    ? companyPerformance[companyId].OverallPerformance
                    : null;
                crdo.IndustryTopPerformers = companyPerformance.ContainsKey(companyId)
                    ? companyPerformance[companyId].IndustryTopPerformers
                    : new List<CompanyScore>();
                crdo.IndustryPeers = companyPerformance.ContainsKey(companyId)
                    ? companyPerformance[companyId].IndustryPeers
                    : new List<CompanyScore>();

                crdo.CompanyHistoricalPerformance = companyHistoricalPerformance.ContainsKey(companyId)
                    ? companyHistoricalPerformance[companyId].Last36Months
                    : new List<CompanyHistoryData>();

                crdo.Analyst = analysts.ContainsKey(corporateData[companyId].AnalystId)
                    ? analysts[corporateData[companyId].AnalystId]
                    : new Analyst();

                crdo.SubIndustryKeyEsgIssues = subIndustryKeyEsgIssues.ContainsKey(subIndustryCode)
                    ? subIndustryKeyEsgIssues[subIndustryCode]
                    : new List<SubIndustryKeyEsgIssue>();
                crdo.KeyEsgIssues = keyEsgIssues ?? new List<KeyEsgIssue>();

                crdo.IndicatorTemplates = indicatorTemplates;

                crdo.ThemesPerformance = companyPerformance.ContainsKey(companyId)
                    ? companyPerformance[companyId].ThemesPerformance
                    : new List<ThemePerformance>();

                crdo.StandardIndicators = standardIndicators.ContainsKey(companyId)
                    ? standardIndicators[companyId]
                    : new List<RPIndicatorExtended>();

                crdo.ControversyIndicators = controversyIndicators.ContainsKey(companyId)
                    ? controversyIndicators[companyId]
                    : new List<RPControversyIndicator>();

                crdo.ComparisonGroupControversies = comparisonGroupControversies.ContainsKey(companyId)
                    ? comparisonGroupControversies[companyId]
                    : new List<ComparisonGroupControversy>();

                crdo.EventIndicators = eventIndicators.Keys.Contains(companyId)
                    ? eventIndicators[companyId]
                    : new List<EventIndicator>();

                crdo.Incidents = incidents.Keys.Contains(companyId)
                    ? incidents[companyId]
                    : new List<Incident>();

                crdo.Sources = sources.ContainsKey(companyId)
                    ? sources[companyId]
                    : new List<Source>();


                ComputeMarketCapitalization(crdo);

                crdos.Add(crdo);
            };

            return crdos;
        }


        private static void ComputeMarketCapitalization(CompanyReportDataOffline crdo)
        {
            decimal min = 0;
            decimal max = 0;

            if (crdo.IndustryPeers != null && crdo.IndustryPeers.Any())
            {
                var minMarkerCap = crdo.IndustryPeers.Min(p => p.MarkerCapitalization);
                var maxMarkerCap = crdo.IndustryPeers.Max(p => p.MarkerCapitalization);

                if (minMarkerCap > 0)
                    min = (decimal)minMarkerCap / 1000;

                if (maxMarkerCap > 0)
                    max = (decimal)maxMarkerCap / 1000;
            }

            crdo.IndustryPeersMinCapitalization = min;
            crdo.IndustryPeersMaxCapitalization = max;
        }
    }
}
